package com.xunan.demo.service;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import lombok.extern.slf4j.Slf4j;
import com.xunan.demo.pojo.CommonResult;
import com.xunan.demo.pojo.WeChatPaymentOrder;
import com.xunan.demo.pojo.WechatPaymentData;
import com.xunan.demo.utils.SignUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Map;

@Slf4j
@Service
public class WechatPaymentService {

    /**
     * 微信支付商户号
     */
    @Value("${wechat-payment.merchant-id}")
    String PAYMENT_MERCHANT_ID;
    /**
     * 微信支付API V3秘钥
     */
    @Value("${wechat-payment.api-v3-key}")
    String PAYMENT_API_V_3_KEY;

    /**
     * 微信支付小程序APP ID
     */
    @Value("${wechat-payment.wechat-mini-app.app-id}")
    String WECHAT_MINI_APP_APP_ID;

    /**
     * 支付回调地址
     */
    @Value("${wechat-payment.notify-url}")
    String PAYMENT_NOTIFY_URL;

    /**
     * 验证器
     */
    @Resource
    Verifier wechatVerifier;

    /**
     * 私钥
     */
    @Resource
    PrivateKey wechatMerchantPrivateKey;

    /**
     * Http客户端
     */
    @Resource
    CloseableHttpClient wechatHttpClient;


    /**
     * 验证签名请求头
     *
     * @param headers      HTTP请求头
     * @param responseBody 应答主体
     * @return 是否通过验证
     */
    public Boolean verifyHttpSign(Map<String, String> headers, String responseBody) {

        /*
         * 注意，所有的HTTP请求的header都是小写字母
         * 微信文档中给的不对
         * */

        log.info("获取到的平台Header");
        log.info(String.valueOf(headers));

        //获取可用的微信平台证书
        X509Certificate wechatCertificate = wechatVerifier.getValidCertificate();
        //获取微信平台证书的序列号（16进制转换）
        String wechatCertificateSerialNumber = wechatCertificate.getSerialNumber().toString(16);

        //获取传递的证书序列号
        String certSerial = headers.get("wechatpay-serial");
        //验证证书序列号（忽略大小写）
        if (!certSerial.equalsIgnoreCase(wechatCertificateSerialNumber)) {
            log.error("证书序列号不一致");
            return false;
        }

        //应答时间戳（这里一定要写）
        String timeStamp = headers.get("wechatpay-timestamp");
        //应答随机串
        String nonce = headers.get("wechatpay-nonce");

        //构建应答的验签字符串
        String signText = timeStamp + "\n" + nonce + "\n" + responseBody + "\n";
        log.info("应答的验签字符串");
        log.info(signText);

        //微信支付的应答签名（BASE64加密后）
        String base64Signature = headers.get("wechatpay-signature");

        //获取签名
        byte[] sign = Base64.decodeBase64(base64Signature);

        //验证
        return SignUtils.verifySha256WithRsa(signText, sign, wechatCertificate.getPublicKey());
    }

    /**
     * 查询支付记录
     *
     * @param transactionId  微信支付订单号
     * @param outTradeNumber 商户订单号
     * @return 支付记录
     */
    public CommonResult<JSONObject> getTransaction(String transactionId, String outTradeNumber) {
        String url;

        if (transactionId != null) {
            //微信支付订单号模式
            url = "https://api.mch.weixin.qq.com/v3/pay/transactions/id/transaction_id?mchid=merchantId";
            url = url.replace("transaction_id", transactionId);
            url = url.replace("merchantId", PAYMENT_MERCHANT_ID);
        } else if (outTradeNumber != null) {
            //商户订单号模式
            url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/out_trade_no?mchid=merchantId";
            url = url.replace("out_trade_no", outTradeNumber);
            url = url.replace("merchantId", PAYMENT_MERCHANT_ID);
        } else {
            return new CommonResult<>(false, "参数不足，无法获取信息", null);
        }

        //组装请求参数
        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader("Accept", "application/json");
        httpGet.addHeader("Content-type", "application/json; charset=utf-8");

        //发送数据
        CloseableHttpResponse response = null;
        try {
            response = wechatHttpClient.execute(httpGet);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //解析数据
        try {
            if (response != null) {
                String bodyAsString = EntityUtils.toString(response.getEntity());
                return new CommonResult<>(true, "成功", JSON.parseObject(bodyAsString));
            } else {
                return new CommonResult<>(false, "无法获取信息", null);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new CommonResult<>(false, "内部错误", null);
        }
    }


    /**
     * 处理支付回调
     *
     * @param jsonObject 数据
     */
    public void paymentNotify(JSONObject jsonObject) {
        //获取通知数据
        JSONObject resource = jsonObject.getJSONObject("resource");
        //附加数据
        String associatedData = resource.getString("associated_data");
        //随机串
        String nonce = resource.getString("nonce");
        //数据密文
        String ciphertext = resource.getString("ciphertext");

        //获得解密数据
        String decryptString = SignUtils.decryptWechatPaymentResponseBody(PAYMENT_API_V_3_KEY, associatedData, nonce, ciphertext);
        //解析数据
        JSONObject decryptData = JSONObject.parseObject(decryptString);

        log.info("获得解析数据：");
        log.info(String.valueOf(decryptData));
    }

    /**
     * 获取微信支付数据
     *
     * @param userOpenId  用户微信OpenId
     * @param description 描述
     * @param totalAmount 总价
     * @return 支付数据
     */
    public CommonResult<WechatPaymentData> getPayData(String userOpenId, String description, Integer totalAmount) {
        //生成订单号
        String outTradeNumber = IdUtil.simpleUUID();

        //组装订单数据
        WeChatPaymentOrder paymentOrder = new WeChatPaymentOrder(
                description,
                outTradeNumber,
                PAYMENT_NOTIFY_URL,
                totalAmount,
                userOpenId
        );

        //获取PrePayId
        String prePayID = generatePrePayId(paymentOrder);
        if (prePayID == null) {
            return new CommonResult<>(false, "无法获取PrePayID", null);
        }

        //获取当前时间戳（微信要求10位）
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        //订单详情扩展字符串
        String orderPackage = "prepay_id=" + prePayID;
        //签名方式
        String SIGN_TYPE = "RSA";
        //待签名字符串
        String paySignText = WECHAT_MINI_APP_APP_ID + "\n" + timeStamp + "\n" + outTradeNumber + "\n" + orderPackage + "\n";
        log.info("待签名字符串：");
        log.info(paySignText);

        //签名结果
        String signResult;

        try {
            //使用商户私钥对待签字符串进行SHA256 with RSA签名
            byte[] sign256 = SignUtils.signSha256WithRsa(paySignText, wechatMerchantPrivateKey);
            //对签名结果进行Base64编码得到签名值
            signResult = Base64.encodeBase64String(sign256);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        log.info("签名结果：");
        log.info(signResult);

        return new CommonResult<>(
                true,
                "成功",
                new WechatPaymentData(
                        timeStamp,
                        outTradeNumber,
                        orderPackage,
                        SIGN_TYPE,
                        signResult
                ));

    }

    /**
     * 生成PrePayId
     *
     * @param data 支付数据
     * @return 获取PrePayId
     */
    private String generatePrePayId(WeChatPaymentOrder data) {

        try {

            //组装数据
            JSONObject rootNode = new JSONObject();

            //应用ID
            rootNode.put("appid", WECHAT_MINI_APP_APP_ID);
            //直连商户号
            rootNode.put("mchid", PAYMENT_MERCHANT_ID);
            //商品描述 string[1,127]
            rootNode.put("description", data.getDescription());
            //商户订单号 string[6,32]
            //商户系统内部订单号，只能是数字、大小写字母_-*且在同一个商户号下唯一
            rootNode.put("out_trade_no", data.getOutTradeNumber());
            //通知地址
            rootNode.put("notify_url", data.getNotifyUrl());

            //订单金额
            JSONObject amountObject = new JSONObject();
            amountObject.put("total", data.getTotalAmount());
            rootNode.put("amount", amountObject);

            //支付者
            JSONObject payerObject = new JSONObject();
            payerObject.put("openid", data.getUserOpenId());
            rootNode.put("payer", payerObject);

            //组装请求参数
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            httpPost.setEntity(new StringEntity(rootNode.toString(), "UTF-8"));

            //发送数据
            CloseableHttpResponse response = wechatHttpClient.execute(httpPost);

            //解析数据
            String bodyAsString = EntityUtils.toString(response.getEntity());
            JSONObject jsonObject = JSON.parseObject(bodyAsString);
            String prePayId = jsonObject.getString("prepay_id");

            if (prePayId != null) {
                log.info("已生成PrePayId：" + prePayId);
                return prePayId;
            } else {
                log.error("无法获取PrePayId，错误信息：" + bodyAsString);
                return null;
            }

        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        }

    }

}
